
#region Apache Notice
/*****************************************************************************
 * $Revision: 591621 $
 * $LastChangedDate: 2007-11-03 07:44:57 -0600 (Sat, 03 Nov 2007) $
 * $LastChangedBy: gbayon $
 * 
 * iBATIS.NET Data Mapper
 * Copyright (C) 2006/2005 - The Apache Software Foundation
 *  
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 ********************************************************************************/
#endregion

using System.Collections.Generic;
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Text;
using IBatisNet.Common;
using IBatisNet.Common.Utilities;
using IBatisNet.Common.Utilities.Objects;
using IBatisNet.Common.Utilities.Objects.Members;
using IBatisNet.DataMapper.Configuration.Cache;
using IBatisNet.DataMapper.Configuration.ParameterMapping;
//// using IBatisNet.DataMapper.Configuration.ResultMapping;
using IBatisNet.DataMapper.DataExchange;
using IBatisNet.DataMapper.Exceptions;
using IBatisNet.DataMapper.MappedStatements;
using IBatisNet.DataMapper.SessionStore;
using IBatisNet.DataMapper.TypeHandlers;
using IBatisNet.DataMapper.Scope;
using System.Reflection;

namespace IBatisNet.DataMapper
{
	/// <summary>
	/// Summary description for SqlMap.
	/// </summary>
	public class SqlMapper : ISqlMapper
	{

		#region Fields
		//(MappedStatement Name, MappedStatement)
		private HybridDictionary _mappedStatements = new HybridDictionary();
		////(ResultMap name, ResultMap)
		//private HybridDictionary _resultMaps = new HybridDictionary();
		//(ParameterMap name, ParameterMap)
		private HybridDictionary _parameterMaps = new HybridDictionary();
		// DataSource
        private IDataSource _dataSource = null;
		//(CacheModel name, cache))
		private HybridDictionary _cacheMaps = new HybridDictionary();
		private TypeHandlerFactory _typeHandlerFactory = null; 
        private DBHelperParameterCache _dbHelperParameterCache = null;

		private bool _cacheModelsEnabled = false;
		// An identifiant 
		private string _id = string.Empty;

		/// <summary>
		/// Container session unique for each thread. 
		/// </summary>
        private ISessionStore _sessionStore = null;
        private IObjectFactory _objectFactory = null;
        private AccessorFactory _accessorFactory = null;
        private DataExchangeFactory _dataExchangeFactory = null;
		#endregion

		#region Properties

        /// <summary>
        /// Name used to identify the the <see cref="SqlMapper"/>
        /// </summary>
        public string Id
        {
            get { return _id; }
        }
	    
        /// <summary>
        /// Allow to set a custom session store like the <see cref="HybridWebThreadSessionStore"/>
        /// </summary>
        /// <remarks>Set it after the configuration and before use of the <see cref="SqlMapper"/></remarks>
        /// <example>
        /// sqlMapper.SessionStore = new HybridWebThreadSessionStore( sqlMapper.Id );
        /// </example>
        public ISessionStore SessionStore
        {
            set { _sessionStore = value; }
        }
	    
		/// <summary>
		///  Returns the DalSession instance 
		///  currently being used by the SqlMap.
		/// </summary>
		public ISqlMapSession LocalSession
		{
			get { return _sessionStore.LocalSession; }
		}

        /// <summary>
        /// Gets a value indicating whether this instance is session started.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is session started; otherwise, <c>false</c>.
        /// </value>
		public bool IsSessionStarted
		{
			get { return (_sessionStore.LocalSession != null); }
		}

        /// <summary>
        /// Gets the DB helper parameter cache.
        /// </summary>
        /// <value>The DB helper parameter cache.</value>
        public DBHelperParameterCache DBHelperParameterCache
        {
            get { return _dbHelperParameterCache; }
        }

		/// <summary>
		/// Factory for DataExchange objects
		/// </summary>
        public DataExchangeFactory DataExchangeFactory
		{
			get { return _dataExchangeFactory; }
		}

		/// <summary>
		/// The TypeHandlerFactory
		/// </summary>
        public TypeHandlerFactory TypeHandlerFactory
		{
			get { return _typeHandlerFactory; }
		}

        /// <summary>
        /// The meta factory for object factory
        /// </summary>
        public IObjectFactory ObjectFactory
        {
            get { return _objectFactory; }
        }

        /// <summary>
        /// The factory which build <see cref="IAccessor"/>
        /// </summary>
        public AccessorFactory AccessorFactory
        {
            get { return _accessorFactory; }
        }

        /// <summary>
        /// A flag that determines whether cache models were enabled 
        /// when this SqlMap was built.
        /// </summary>
        public bool IsCacheModelsEnabled
        {
			set { _cacheModelsEnabled = value; }
            get { return _cacheModelsEnabled; }
        }

		#endregion

		#region Constructor (s) / Destructor


        /// <summary>
        /// Initializes a new instance of the <see cref="SqlMapper"/> class.
        /// </summary>
        /// <param name="objectFactory">The object factory.</param>
        /// <param name="accessorFactory">The accessor factory.</param>
        public SqlMapper(IObjectFactory objectFactory,
            AccessorFactory accessorFactory) 
		{
            _typeHandlerFactory = new TypeHandlerFactory();
            _dbHelperParameterCache = new DBHelperParameterCache();
            _objectFactory = objectFactory;
            _accessorFactory = accessorFactory;

            _dataExchangeFactory = new DataExchangeFactory(_typeHandlerFactory, _objectFactory, accessorFactory);
			_id = HashCodeProvider.GetIdentityHashCode(this).ToString();
            _sessionStore = SessionStoreFactory.GetSessionStore(_id);
		}
		#endregion
         
         
		#region Get/Add ParemeterMap, ResultMap, MappedStatement, TypeAlias, DataSource, CacheModel

		/// <summary>
		/// Gets a MappedStatement by name
		/// </summary>
		/// <param name="id"> The id of the statement</param>
		/// <returns> The MappedStatement</returns>
		public IMappedStatement GetMappedStatement(string id) 
		{
			if (_mappedStatements.Contains(id) == false) 
			{
				throw new DataMapperException("This SQL map does not contain a MappedStatement named " + id);
			}
			return (IMappedStatement) _mappedStatements[id];
		}

		/// <summary>
		/// Adds a (named) MappedStatement.
		/// </summary>
		/// <param name="key"> The key name</param>
		/// <param name="mappedStatement">The statement to add</param>
		public void AddMappedStatement(string key, IMappedStatement mappedStatement) 
		{
			if (_mappedStatements.Contains(key) == true) 
			{
				throw new DataMapperException("This SQL map already contains a MappedStatement named " + mappedStatement.Id);
			}
			_mappedStatements.Add(key, mappedStatement);
		}

		/// <summary>
		/// The MappedStatements collection
		/// </summary>
        public HybridDictionary MappedStatements
		{
			get { return _mappedStatements; }
		}

		/// <summary>
		/// Get a ParameterMap by name
		/// </summary>
		/// <param name="name">The name of the ParameterMap</param>
		/// <returns>The ParameterMap</returns>
		public ParameterMap GetParameterMap(string name) 
		{
			if (!_parameterMaps.Contains(name)) 
			{
				throw new DataMapperException("This SQL map does not contain an ParameterMap named " + name + ".  ");
			}
			return (ParameterMap) _parameterMaps[name];
		}

		/// <summary>
		/// Adds a (named) ParameterMap.
		/// </summary>
		/// <param name="parameterMap">the ParameterMap to add</param>
        public void AddParameterMap(ParameterMap parameterMap) 
		{
			if (_parameterMaps.Contains(parameterMap.Id) == true) 
			{
				throw new DataMapperException("This SQL map already contains an ParameterMap named " + parameterMap.Id);
			}
			_parameterMaps.Add(parameterMap.Id, parameterMap);
		}

		///// <summary>
		///// Gets a ResultMap by name
		///// </summary>
		///// <param name="name">The name of the result map</param>
		///// <returns>The ResultMap</returns>
  //      public IResultMap GetResultMap(string name) 
		//{
		//	if (_resultMaps.Contains(name) == false) 
		//	{
		//		throw new DataMapperException("This SQL map does not contain an ResultMap named " + name);
		//	}
		//	return (ResultMap) _resultMaps[name];
		//}

		///// <summary>
		///// Adds a (named) ResultMap
		///// </summary>
		///// <param name="resultMap">The ResultMap to add</param>
  //      public void AddResultMap(IResultMap resultMap) 
		//{
		//	if (_resultMaps.Contains(resultMap.Id) == true) 
		//	{
		//		throw new DataMapperException("This SQL map already contains an ResultMap named " + resultMap.Id);
		//	}
		//	_resultMaps.Add(resultMap.Id, resultMap);
		//}

		/// <summary>
		/// The ParameterMap collection
		/// </summary>
		public HybridDictionary ParameterMaps
		{
			get { return _parameterMaps; }
		}

		///// <summary>
		///// The ResultMap collection
		///// </summary>
  //      public HybridDictionary ResultMaps
		//{
		//	get { return _resultMaps; }
		//}

		/// <summary>
		/// The DataSource
		/// </summary>
        public IDataSource DataSource
		{
			get { return  _dataSource; }
			set { _dataSource = value; }
		}

		/// <summary>
		/// Flushes all cached objects that belong to this SqlMap
		/// </summary>
		public void FlushCaches() 
		{
			IDictionaryEnumerator enumerator = _cacheMaps.GetEnumerator();
			while (enumerator.MoveNext())
			{
				((CacheModel)enumerator.Value).Flush();
			}
		}

		/// <summary>
		/// Adds a (named) cache.
		/// </summary>
		/// <param name="cache">The cache to add</param>
		public void AddCache(CacheModel cache) 
		{
			if (_cacheMaps.Contains(cache.Id)) 
			{
				throw new DataMapperException("This SQL map already contains an Cache named " + cache.Id);
			}
			_cacheMaps.Add(cache.Id, cache);
		}

		/// <summary>
		/// Gets a cache by name
		/// </summary>
		/// <param name="name">The name of the cache to get</param>
		/// <returns>The cache object</returns>
        public CacheModel GetCache(string name) 
		{
			if (!_cacheMaps.Contains(name)) 
			{
				throw new DataMapperException("This SQL map does not contain an Cache named " + name);
			}
			return (CacheModel) _cacheMaps[name];
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public string GetDataCacheStats() 
		{
			StringBuilder buffer = new StringBuilder();
			buffer.Append(Environment.NewLine);
			buffer.Append("Cache Data Statistics");
			buffer.Append(Environment.NewLine);
			buffer.Append("=====================");
			buffer.Append(Environment.NewLine);

			IDictionaryEnumerator enumerator = _mappedStatements.GetEnumerator();
			while (enumerator.MoveNext()) 
			{
				IMappedStatement mappedStatement = (IMappedStatement)enumerator.Value;

				buffer.Append(mappedStatement.Id);
				buffer.Append(": ");

				if (mappedStatement is CachingStatement)
				{
					double hitRatio = ((CachingStatement)mappedStatement).GetDataCacheHitRatio();
					if (hitRatio != -1) 
					{
						buffer.Append(Math.Round(hitRatio * 100));
						buffer.Append("%");
					} 
					else 
					{
						// this statement has a cache but it hasn't been accessed yet
						// buffer.Append("Cache has not been accessed."); ???
						buffer.Append("No Cache.");
					}
				}
				else
				{
					buffer.Append("No Cache.");
				}

				buffer.Append(Environment.NewLine);
			}

			return buffer.ToString();
		}

		#endregion

  
	}
}
